// Copyright 2008 web2me.dev
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

package net.web2me.api;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import org.apache.log4j.Logger;

import net.web2me.api.util.ClassPathHacker;
import net.web2me.api.util.JarFileFilter;

/**
 * Creates {@link IParser} implementations
 * @author web2me
 *
 */
public class ParserFactory {

	private static Logger logger = Logger.getLogger(ParserFactory.class);
	private static IParser parserInstance = null;
	private static File parsersDirectory;

	/**
	 * Gets the directory where the parsers are placed
	 * @return A directory
	 */
	public static File getParsersDirectory() {
		return parsersDirectory;
	}

	/**
	 * Establish the directory where the parsers are placed
	 * @param parsersDirectory
	 */
	public static void setParsersDirectory(File parsersDirectory) {
		ParserFactory.parsersDirectory = parsersDirectory;
	}

	public static void setParsersResourcePath(String path) {

	}
	
	/**
	 * Reads all the parser.properties file of each parser and return it as a list.<br>
	 * This is usefull to provide a list of available parsers to the user.
	 * @return A perser.properties list of files
	 * @throws ParserFactoryException 
	 * @throws MalformedURLException 
	 */
	public static List<Properties> getParsersPropertyFiles() throws ParserFactoryException, MalformedURLException{
		String[] parsersJar = getParserJars();
		Properties parser = null;
		URL parserDescriptor = null;
		List<Properties> propertyFiles = new ArrayList<Properties>();

		for (String jar : parsersJar) {
			parserDescriptor = getParserDescriptor(jar);
			
			parser = new Properties();
			
			try {
				parser.load(parserDescriptor.openStream());
				propertyFiles.add(parser);
			} catch (IOException e) {
				throw new ParserFactoryException("A problem occured while reading properties file");
			}
		}
		
		
		return propertyFiles;
	}
	
	private static String[] getParserJars() throws ParserFactoryException{
		if (!parsersDirectory.exists()) {
			throw new ParserFactoryException(
					"The parsers directory doesn't exist");
		}

		String[] parsersJar = parsersDirectory.list(new JarFileFilter());
		logger.debug(parsersJar.length + " jars found");
		
		return parsersJar;
	}
	
	private static URL getParserDescriptor(String jar) throws ParserFactoryException, MalformedURLException{
		File jarFile = new File(parsersDirectory + "/" + jar);
		URLClassLoader loader = null;
		
		logger.debug("Reading file \"" + jarFile.toURI().toURL().toString() + "\"");
		
		if(!jarFile.exists() || !jarFile.canRead()){
			throw new ParserFactoryException("The file " + jarFile.toString() + " isn't accesible");
		}
		
		loader = new URLClassLoader(new URL[] { jarFile.toURI().toURL() });

		return loader.findResource("parser.properties");
	}

	/**
	 * Returns the correct parser according to the configuration file
	 * 
	 * @param id The parser name
	 * @return An instance of IParser
	 * @throws ParserFactoryException
	 * @throws IOException 
	 * @throws ParserException 
	 * @throws MalformedURLException 
	 * @throws MalformedURLException 
	 */
	public static IParser getParser(String id) throws ParserFactoryException, ParserException, MalformedURLException {
		String[] parsersJar = getParserJars();

		for (String jar : parsersJar) {
			File jarFile = new File(parsersDirectory + "/" + jar);
			URL parserDescriptor = getParserDescriptor(jar);
			logger.debug("Parser descriptor \"" + parserDescriptor.toString() + "\"");

			if(parserDescriptor == null){
				logger.debug("Could not find parser.properties in \"" + jar + "\"");
				continue;
			}
			
			String clazz = getParserClass(parserDescriptor, id);
			logger.debug("The class is \"" + clazz + "\"");
			
			if(clazz != ""){
				logger.debug("Parser \"" + id + "\" associated with class \"" + clazz + "\"");
				try {
					ClassPathHacker.addFile(jarFile);
					parserInstance = (IParser) Class.forName(clazz).newInstance();
					break;
				} catch (InstantiationException e) {
					logger.fatal("InstantiationException caught", e);
					throw new ParserFactoryException(e);
				} catch (IllegalAccessException e) {
					logger.fatal("IllegalAccessException caught", e);
					throw new ParserFactoryException(e);
				} catch (ClassNotFoundException e) {
					logger.fatal("ClassNotFoundException caught", e);
					throw new ParserFactoryException(e);
				}
			}
		}
		
		if(parserInstance == null){
			throw new ParserFactoryException("The parser \"" + id + "\" couldn't be found");
		}
		
		return parserInstance;
	}
	
	private static String getParserClass(URL parserDescriptor, String id) throws ParserFactoryException{
		Properties parser = new Properties();
		
		try {
			parser.load(parserDescriptor.openStream());
		} catch (IOException e) {
			throw new ParserFactoryException("A problem occured while reading properties file");
		}
		
		String parserId = parser.getProperty("parser.id");
		
		logger.debug("Reading \"" + parserId + "\" from properties file");

		if (parserId.equals(id)) {
			return parser.getProperty("parser.class");
		}
		
		return "";

	}
}
